# create by fanfan on 2020/3/31 0031
import keras
from keras.datasets import mnist
from keras.models import  Sequential
from keras.layers import Dense,Dropout,Activation,Flatten
from keras.layers import Conv2D,MaxPool2D
from keras.wrappers.scikit_learn import KerasClassifier
from keras import backend as K
from sklearn.model_selection import GridSearchCV

num_classes = 10
img_rows,img_cols = 28,28

# load training data and do basic data normalization
(x_train, y_train), (x_test, y_test) = mnist.load_data()
if K.image_data_format() == 'channels_first':
    x_train = x_train.reshape(x_train.shape[0],1,img_rows,img_cols)
    x_test = x_test.reshape(x_test.shape[0],1,img_rows,img_cols)
    input_shape = (1,img_rows,img_cols)
else:
    x_train = x_train.reshape(x_train.shape[0],img_rows,img_cols,1)
    x_test = x_test.reshape(x_test.shape[0],img_rows,img_cols,1)
    input_shape = (img_rows,img_cols,1)

x_train = x_train.astype('float32')
x_test = x_test.astype("float32")
x_train /= 255
x_test /= 255


# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train,num_classes)
y_test = keras.utils.to_categorical(y_test,num_classes)

def make_model(dense_layer_sizes,filters,kernel_size,pool_size):
    '''Creates model comprised of 2 convolutional layers followed by dense layers
        dense_layer_sizes: List of layer sizes.
            This list has one number for each layer
        filters: Number of convolutional filters in each convolutional layer
        kernel_size: Convolutional kernel size
        pool_size: Size of pooling area for max pooling
        '''

    model = Sequential()
    model.add(Conv2D(filters,kernel_size,
                     padding='valid',
                     input_shape=input_shape))
    model.add(Activation('relu'))
    model.add(Conv2D(filters,kernel_size))
    model.add(Activation('relu'))
    model.add(MaxPool2D(pool_size=pool_size))
    model.add(Dropout(0.25))

    model.add(Flatten())

    for layer_size in dense_layer_sizes:
        model.add(Dense(layer_size))
        model.add(Activation('relu'))

    model.add(Dropout(0.5))
    model.add(Dense(num_classes))
    model.add(Activation('softmax'))

    model.compile(loss='categorical_crossentropy',
                  optimizer='adadelta',
                  metrics=['accuracy']
                  )
    return model

dense_size_candidates = [[32],[64],[32,32],[64,64]]
my_classifier = KerasClassifier(make_model,batch_size =32)
validator = GridSearchCV(
    my_classifier,
    param_grid={
        'dense_layer_sizes':dense_size_candidates,
        'epochs': [3, 6],
        'filters': [8],
        'kernel_size': [3],
        'pool_size': [2]},
    scoring='neg_log_loss',
    n_jobs=1
)
validator.fit(x_train,y_train)
print('The parameters of the best model are: ')
print(validator.best_params_)

# validator.best_estimator_ returns sklearn-wrapped version of best model.
# validator.best_estimator_.model returns the (unwrapped) keras model
best_model = validator.best_estimator_.model
metric_names = best_model.metrics_names
metric_values = best_model.evaluate(x_test, y_test)
for metric, value in zip(metric_names, metric_values):
    print(metric, ': ', value)


